package grade.calculate.read;

import grade.calculate.pojo.Data;
import grade.calculate.pojo.Student;
import lombok.NoArgsConstructor;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 学业表现分读取
 * <p>
 * 注意继承后还必须写传入参数为Data的构造方法并在构造方法内通过super(data)
 * 获取到启动时配置的参数和其它事件处理结果数据
 * </p>
 *
 * @author fromdrowning
 * @date 2024/2/20 19:34
 */
@NoArgsConstructor
public class Read extends AbstractRead {

    /**
     * 带参构造方法
     * <p>
     * 通过super(data)获取到启动时配置的参数和其它事件处理结果数据 初始化资源
     * </p>
     *
     * @param data {@link Data}用于各个事件之间获取数据
     */
    public Read(Data data) {
        super(data);
    }

    /**
     * 学业表现分读取
     *
     * @throws IOException 找不到读取的文件时抛出此异常
     */
    @Override
    protected void read() throws IOException {
        try (Workbook workbook = readFile(new FileInputStream(inputFilePath))) {
            // 根据启动时配置读取Excel文件的表 读取该表
            Sheet sheet = workbook.getSheetAt(sheetNum);

            readEachStudent(sheet);

            // 将读取后学生信息集合设置到Data中 方便其它事件获取
            data.setStudentList(studentList);
        }
    }

    /**
     * 读取Excel文件 推断文件类型 根据不同的文件生成对应的类
     *
     * @param inputStream 读取的Excel文件对象
     * @return {@link Workbook} 读取到的Excel文件
     * @throws IOException 异常向上抛出到read方法
     */
    private Workbook readFile(FileInputStream inputStream) throws IOException {
        String fileType = inputFileName.substring(inputFileName.lastIndexOf("."));

        return switch (fileType) {
            case ".xls" -> new HSSFWorkbook(inputStream);
            case ".xlsx" -> new XSSFWorkbook(inputStream);
            default -> throw new RuntimeException("必须为.xls或.xlsx结尾的文件");
        };
    }

    /**
     * 读取所有学生
     *
     * @param sheet 读取到的表
     */
    private void readEachStudent(Sheet sheet) {
        // 遍历整张表 读取所有学生
        for (int i = 2; i < sheet.getLastRowNum(); i = i + 43) {
            Row row = sheet.getRow(i);
            Cell name = row.getCell(12);

            // 读取到学生 获取对应的信息
            if (name != null) {
                Student student = Student.builder()
                        .name(name.toString())
                        .build();

                readInfo(student, sheet, i);

                // 将结果添加到学生信息集合中
                studentList.add(student);
            }
        }
    }

    /**
     * 根据姓名读取学生对应的信息 排除公选课
     *
     * @param sheet   读取到的表
     * @param student 学生信息
     * @param i       学生姓名对应的行数
     */
    private void readInfo(Student student, Sheet sheet, int i) {
        // 成绩信息行在姓名的下面3行
        i = i + 3;

        // 初始化存放成绩信息的Map
        Map<String, List<Double>> infoMap = new HashMap<>();

        while (true) {
            // 读取该学生的课程
            Row row = sheet.getRow(i);
            String course = row.getCell(0).getStringCellValue();

            // 读取到"以下空白"退出循环
            if ("以下空白".equals(course)) {
                student.setInfo(infoMap);
                break;
            }

            if (!semesterCourseList.contains(course)) {
                // 继续循环下一行
                i++;
                continue;
            }

            // 获取到对应的课程 学分 成绩
            String credit = row.getCell(7).getStringCellValue();
            String score = row.getCell(8).getStringCellValue();

            score = coverSpecialScore(score, course);

            // 创建存放学分和成绩的集合
            List<Double> creditAndScoreList = new ArrayList<>();

            creditAndScoreList.add(Double.valueOf(credit));
            creditAndScoreList.add(Double.valueOf(score));
            infoMap.put(course, creditAndScoreList);

            // 继续循环下一行
            i++;
        }
    }

    /**
     * 根据课程转换特殊的成绩
     *
     * @param score  未转换的成绩
     * @param course 课程
     * @return {@link String} 转换后的成绩
     */
    private String coverSpecialScore(String score, String course) {
        // 成绩需要去掉*号
        if (score.contains("*")) {
            score = score.substring(0, score.lastIndexOf("*"));
        }

        // 军事技能需要根据等级换算成对应的分数
        if ("军事技能".equals(course) || "劳动教育".equals(course)) {
            score = switch (score) {
                case "优" -> "95";
                case "良" -> "85";
                case "中" -> "75";
                case "及格" -> "65";
                case "不及格" -> "59";
                default -> "0";
            };
        }

        return score;
    }
}
